/*
Copyright 2011 The Perkeep Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package index

import (
	"net/url"
	"time"

	"perkeep.org/pkg/schema"
	"perkeep.org/pkg/types/camtypes"
)

var urle = url.QueryEscape

func urld(s string) string {
	d, _ := url.QueryUnescape(s)
	return d
}

type dupSkipper struct {
	m map[string]bool
}

// not thread safe.
func (s *dupSkipper) Dup(v string) bool {
	if s.m == nil {
		s.m = make(map[string]bool)
	}
	if s.m[v] {
		return true
	}
	s.m[v] = true
	return false
}

// claimPtrsAttrValue returns the value of attr from claims,
// or the empty string if not found.
// Claims should be sorted by claim.Date.
func claimPtrsAttrValue(claims []*camtypes.Claim, attr string, at time.Time, signerFilter SignerRefSet) string {
	return claimsIntfAttrValue(claimPtrSlice(claims), attr, at, signerFilter)
}

type claimsIntf interface {
	Len() int
	Claim(i int) *camtypes.Claim
}

type claimSlice []camtypes.Claim

func (s claimSlice) Len() int                    { return len(s) }
func (s claimSlice) Claim(i int) *camtypes.Claim { return &s[i] }

type claimPtrSlice []*camtypes.Claim

func (s claimPtrSlice) Len() int                    { return len(s) }
func (s claimPtrSlice) Claim(i int) *camtypes.Claim { return s[i] }

// claimsIntfAttrValue finds the value of an attribute in a list of claims
// or empty string if not found. claims must be non-nil.
// If signerFilter contains any refs, a claim is only taken into account if it
// has been signed by one of the given signer refs.
func claimsIntfAttrValue(claims claimsIntf, attr string, at time.Time, signerFilter SignerRefSet) string {
	if claims == nil {
		panic("nil claims argument in claimsIntfAttrValue")
	}

	if at.IsZero() {
		at = time.Now()
	}

	// use a small static buffer as it speeds up
	// search.BenchmarkQueryPermanodeLocation by 6-7%
	// with go 1.7.1
	var buf [8]string
	v := buf[:][:0]

	for i := 0; i < claims.Len(); i++ {
		cl := claims.Claim(i)
		if cl.Attr != attr || cl.Date.After(at) {
			continue
		}

		if len(signerFilter) > 0 {
			if !signerFilter.blobMatches(cl.Signer) {
				continue
			}
		}

		switch cl.Type {
		case string(schema.DelAttributeClaim):
			if cl.Value == "" {
				v = v[:0]
			} else {
				i := 0
				for _, w := range v {
					if w != cl.Value {
						v[i] = w
						i++
					}
				}
				v = v[:i]
			}
		case string(schema.SetAttributeClaim):
			v = append(v[:0], cl.Value)
		case string(schema.AddAttributeClaim):
			v = append(v, cl.Value)
		}
	}
	if len(v) != 0 {
		return v[0]
	}
	return ""
}
